package keqiang.com.kq_flutter_core_widget.channel;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import com.hjq.permissions.OnPermissionCallback;
import com.hjq.permissions.Permission;
import com.hjq.permissions.XXPermissions;
import com.inuker.bluetooth.library.connect.listener.BleConnectStatusListener;
import com.inuker.bluetooth.library.connect.options.BleConnectOptions;
import com.inuker.bluetooth.library.connect.response.BleNotifyResponse;
import com.inuker.bluetooth.library.connect.response.BleUnnotifyResponse;
import com.inuker.bluetooth.library.connect.response.BleWriteResponse;
import com.inuker.bluetooth.library.model.BleGattCharacter;
import com.inuker.bluetooth.library.model.BleGattDescriptor;
import com.inuker.bluetooth.library.model.BleGattService;
import com.inuker.bluetooth.library.search.SearchRequest;
import com.inuker.bluetooth.library.search.SearchResult;
import com.inuker.bluetooth.library.search.response.SearchResponse;
import com.inuker.bluetooth.library.utils.BluetoothUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.EventChannel;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import keqiang.com.kq_flutter_core_widget.util.BleManager;
import me.zhouzhuo810.magpiex.utils.ByteUtil;
import me.zhouzhuo810.magpiex.utils.EncodeUtil;
import me.zhouzhuo810.magpiex.utils.OSUtils;

import static com.inuker.bluetooth.library.Constants.REQUEST_SUCCESS;
import static com.inuker.bluetooth.library.Constants.STATUS_CONNECTED;

/**
 * 蓝牙操作通道
 *
 * @author zhouzhuo810
 * @date 2024/1/23 14:27
 */
public class NativeBleChannel extends MethodChannel {
    
    // 通道id
    private static final String CHANNEL = "flutter.keqiang.com/ble";
    
    // 蓝牙是否打开
    private static final String METHOD_IS_BLE_OPEN = "isBleOpen";
    // 打开蓝牙
    private static final String METHOD_OPEN_BLE = "openBle";
    // 关闭蓝牙
    private static final String METHOD_CLOSE_BLE = "closeBle";
    
    // 开始扫描蓝牙
    private static final String METHOD_START_SCAN_BLE = "startScanBle";
    // 停止扫描蓝牙
    private static final String METHOD_STOP_SCAN_BLE = "stopScanBle";
    // 连接蓝牙
    private static final String METHOD_CONNECT_DEVICE = "connectDevice";
    // 断开蓝牙
    private static final String METHOD_DISCONNECT_DEVICE = "disConnectDevice";
    // 开始监听特征值
    private static final String METHOD_NOTIFY = "notify";
    // 取消监听特征值
    private static final String METHOD_UN_NOTIFY = "unNotify";
    // 写入数据
    private static final String METHOD_WRITE_CHARACTER_DATA = "writeCharacterData";
    
    private Context mContext;
    private Handler mHandler;
    
    public void setContext(Context context) {
        mContext = context;
        if (mContext != null) {
            registerBle();
        }
    }
    
    public NativeBleChannel(@NonNull BinaryMessenger messenger) {
        super(messenger, CHANNEL);
        init();
    }
    
    private void init() {
        setMethodCallHandler((call, result) -> {
            switch (call.method) {
                case METHOD_IS_BLE_OPEN:
                    result.success(BleManager.getClient().isBluetoothOpened());
                    break;
                case METHOD_OPEN_BLE:
                    if (OSUtils.isAndroid12()) {
                        XXPermissions.with(mContext)
                            .permission(Permission.BLUETOOTH_CONNECT)
                            .request(new OnPermissionCallback() {
                                @Override
                                public void onGranted(@NonNull List<String> permissions, boolean allGranted) {
                                    result.success(true);
                                    Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
                                    ((Activity) mContext).startActivityForResult(intent, 0x05);
                                }
                                
                                @Override
                                public void onDenied(@NonNull List<String> permissions, boolean doNotAskAgain) {
                                    OnPermissionCallback.super.onDenied(permissions, doNotAskAgain);
                                    result.success(false);
                                }
                            });
                    } else {
                        result.success(BleManager.getClient().openBluetooth());
                    }
                    break;
                case METHOD_CLOSE_BLE:
                    if (OSUtils.isAndroid12()) {
                        // Android 12开始没有权限关蓝牙
                        result.success(false);
                    } else {
                        result.success(BleManager.getClient().closeBluetooth());
                    }
                    break;
                case METHOD_START_SCAN_BLE:
                    BluetoothUtils.requestPermission(mContext, new OnPermissionCallback() {
                        @Override
                        public void onGranted(@NonNull List<String> permissions, boolean allGranted) {
                            if (allGranted) {
                                startScanBle(call, result);
                            } else {
                                result.success(false);
                            }
                        }
                        
                        @Override
                        public void onDenied(@NonNull List<String> permissions, boolean doNotAskAgain) {
                            OnPermissionCallback.super.onDenied(permissions, doNotAskAgain);
                            result.success(false);
                        }
                    });
                    break;
                case METHOD_STOP_SCAN_BLE:
                    BleManager.getClient().stopSearch();
                    result.success(true);
                    break;
                case METHOD_CONNECT_DEVICE:
                    connectDevice(call, result);
                    break;
                case METHOD_DISCONNECT_DEVICE:
                    String macAddress = call.argument("macAddress");
                    if (macAddress != null) {
                        BleManager.getClient().disconnect(macAddress);
                    }
                    result.success(true);
                    break;
                case METHOD_NOTIFY:
                    macAddress = call.argument("macAddress");
                    String character = call.argument("character");
                    String service = call.argument("service");
                    if (macAddress != null && character != null && service != null) {
                        BleManager.getClient().notify(macAddress, UUID.fromString(service), UUID.fromString(character), new BleNotifyResponse() {
                            @Override
                            public void onNotify(UUID service, UUID character, byte[] value) {
                                onNotifyData(service, character, value);
                            }
                            
                            @Override
                            public void onResponse(int code) {
                                result.success(true);
                            }
                        });
                    } else {
                        result.success(true);
                    }
                    break;
                case METHOD_UN_NOTIFY:
                    macAddress = call.argument("macAddress");
                    service = call.argument("service");
                    character = call.argument("character");
                    if (macAddress != null && character != null && service != null) {
                        BleManager.getClient().unnotify(macAddress, UUID.fromString(service), UUID.fromString(character), new BleUnnotifyResponse() {
                            @Override
                            public void onResponse(int code) {
                                result.success(true);
                            }
                        });
                    } else {
                        result.success(true);
                    }
                    break;
                case METHOD_WRITE_CHARACTER_DATA:
                    macAddress = call.argument("macAddress");
                    service = call.argument("service");
                    character = call.argument("character");
                    List<Integer> value = call.argument("value");
                    if (macAddress != null && character != null && service != null && value != null) {
                        byte[] bytes = new byte[value.size()];
                        for (int i = 0; i < value.size(); i++) {
                            bytes[i] = value.get(i).byteValue();
                        }
                        Log.e("xxx", "ByteUtil.byteArrayToString(bytes)=" + ByteUtil.byteArrayToString(bytes));
                        BleManager.getClient().write(macAddress, UUID.fromString(service), UUID.fromString(character), bytes, new BleWriteResponse() {
                            @Override
                            public void onResponse(int code) {
                                result.success(code == REQUEST_SUCCESS);
                            }
                        });
                    } else {
                        result.success(true);
                    }
                    break;
            }
        });
    }
    
    private void onNotifyData(UUID service, UUID character, byte[] value) {
        if (mBleNotifyHandler != null) {
            // 这里发送上传进度
            HashMap<String, Object> map = new HashMap<>();
            map.put("service", service.toString());
            map.put("character", character.toString());
            map.put("value", value == null ? null : EncodeUtil.base64Encode2String(value));
            // event回调必须放到主线程
            if (mHandler == null) {
                mHandler = new Handler(Looper.getMainLooper());
            }
            mHandler.post(() -> {
                EventChannel.EventSink eventSink = mBleNotifyHandler.getEventSink();
                if (eventSink != null) {
                    eventSink.success(map);
                }
            });
        }
    }
    
    private BleConnectStatusListener mConnectStateListener = new BleConnectStatusListener() {
        @Override
        public void onConnectStatusChanged(String mac, int status) {
            onConnectedChanged(mac, status == STATUS_CONNECTED ? BluetoothProfile.STATE_CONNECTED : BluetoothProfile.STATE_DISCONNECTED);
        }
    };
    
    private void connectDevice(MethodCall call, Result result) {
        String macAddress = call.argument("macAddress");
        Integer timeoutMills = call.argument("timeoutMills");
        if (macAddress == null) {
            HashMap<String, Object> map = new HashMap<>();
            map.put("connected", false);
            result.success(map);
            return;
        }
        BleManager.getClient().unregisterConnectStatusListener(macAddress, mConnectStateListener);
        BleManager.getClient().registerConnectStatusListener(macAddress, mConnectStateListener);
        BleConnectOptions options = new BleConnectOptions.Builder()
            .setConnectTimeout(timeoutMills == null ? 10 * 1000 : timeoutMills)
            .build();
        BleManager.getClient().connect(macAddress, options, (code, data) -> {
            boolean connected = code == REQUEST_SUCCESS;
            HashMap<String, Object> map = new HashMap<>();
            map.put("connected", connected);
            if (data != null) {
                List<BleGattService> services = data.getServices();
                if (services != null) {
                    List<HashMap<String, Object>> servicesMap = new ArrayList<>();
                    for (BleGattService service : services) {
                        HashMap<String, Object> serviceMap = new HashMap<>();
                        serviceMap.put("uuid", service.getUUID().toString());
                        List<BleGattCharacter> characters = service.getCharacters();
                        if (characters != null) {
                            List<HashMap<String, Object>> charactersMap = new ArrayList<>();
                            for (BleGattCharacter character : characters) {
                                HashMap<String, Object> characterMap = new HashMap<>();
                                characterMap.put("uuid", character.getUuid().toString());
                                List<BleGattDescriptor> descriptors = character.getDescriptors();
                                if (descriptors != null) {
                                    List<HashMap<String, Object>> descriptorsMap = new ArrayList<>();
                                    for (BleGattDescriptor descriptor : descriptors) {
                                        HashMap<String, Object> descriptorMap = new HashMap<>();
                                        descriptorMap.put("uuid", descriptor.getmUuid().toString());
                                        descriptorMap.put("permission", descriptor.getmPermissions());
                                        descriptorMap.put("value", descriptor.getmValue() == null ? null : EncodeUtil.base64Encode2String(descriptor.getmValue()));
                                        descriptorsMap.add(descriptorMap);
                                    }
                                    characterMap.put("descriptors", descriptorsMap);
                                }
                                charactersMap.add(characterMap);
                            }
                            serviceMap.put("characters", charactersMap);
                        }
                        servicesMap.add(serviceMap);
                    }
                    map.put("services", servicesMap);
                }
            }
            result.success(map);
        });
    }
    
    
    private void registerBle() {
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
        intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
        try {
            mContext.registerReceiver(mBleReceiver, intentFilter);
        } catch (Exception ignored) {
        }
    }
    
    private void unRegisterBle() {
        try {
            mContext.unregisterReceiver(mBleReceiver);
        } catch (Exception ignored) {
        }
    }
    
    private static final List<SearchResult> sDevices = new ArrayList<>();
    
    private void startScanBle(MethodCall call, Result result) {
        String tag = call.argument("tag");
        Integer duration = call.argument("duration");
        int times = 4;
        int durationMills = duration == null ? 3000 : duration / times;
        final SearchRequest request = new SearchRequest.Builder()
            .searchBluetoothLeDevice(durationMills, times)   // 先扫BLE设备1次，每次10s
            .build();
        BleManager.getClient().stopSearch();
        result.success(true);
        sDevices.clear();
        BleManager.getClient().search(request, new SearchResponse() {
            @Override
            public void onSearchStarted() {
            }
            
            @Override
            public void onDeviceFounded(SearchResult device) {
                if (!sDevices.contains(device)) {
                    sDevices.add(device);
                    onDeviceFindOrScanComplete(tag, false, device);
                }
            }
            
            @Override
            public void onSearchStopped() {
                sDevices.clear();
                onDeviceFindOrScanComplete(tag, true, null);
            }
            
            @Override
            public void onSearchCanceled() {
                sDevices.clear();
                onDeviceFindOrScanComplete(tag, true, null);
            }
        });
    }
    
    private void onDeviceFindOrScanComplete(String tag, boolean isComplete, @Nullable SearchResult device) {
        if (mBleScanHandler != null) {
            // 这里发送上传进度
            HashMap<String, Object> map = new HashMap<>();
            map.put("tag", tag);
            map.put("isComplete", isComplete);
            map.put("name", device == null ? null : device.getName());
            map.put("macAddress", device == null ? null : device.getAddress());
            map.put("rssi", device == null ? null : device.rssi);
            if (device != null && device.device != null) {
                int connectionState = BluetoothUtils.getBluetoothManager().getConnectionState(device.device, BluetoothProfile.GATT);
                map.put("connectState", connectionState);
            }
            // event回调必须放到主线程
            if (mHandler == null) {
                mHandler = new Handler(Looper.getMainLooper());
            }
            mHandler.post(() -> {
                EventChannel.EventSink eventSink = mBleScanHandler.getEventSink();
                if (eventSink != null) {
                    eventSink.success(map);
                }
            });
        }
    }
    
    private void onBleEnableChanged(boolean enabled) {
        if (mBleEnableHandler != null) {
            // 这里发送上传进度
            HashMap<String, Object> map = new HashMap<>();
            map.put("enable", enabled);
            // event回调必须放到主线程
            if (mHandler == null) {
                mHandler = new Handler(Looper.getMainLooper());
            }
            mHandler.post(() -> {
                EventChannel.EventSink eventSink = mBleEnableHandler.getEventSink();
                if (eventSink != null) {
                    eventSink.success(map);
                }
            });
        }
    }
    
    private void onConnectedChanged(String macAddress, int connectState) {
        if (mBleConnectStateHandler != null) {
            // 这里发送上传进度
            HashMap<String, Object> map = new HashMap<>();
            map.put("macAddress", macAddress);
            map.put("connectState", connectState);
            // event回调必须放到主线程
            if (mHandler == null) {
                mHandler = new Handler(Looper.getMainLooper());
            }
            mHandler.post(() -> {
                EventChannel.EventSink eventSink = mBleConnectStateHandler.getEventSink();
                if (eventSink != null) {
                    eventSink.success(map);
                }
            });
        }
    }
    
    private NativeBleNotifyHandler mBleNotifyHandler;
    private NativeBleScanHandler mBleScanHandler;
    private NativeBleEnableHandler mBleEnableHandler;
    private NativeBleConnectStateHandler mBleConnectStateHandler;
    
    public void setScanHandler(NativeBleScanHandler bleScanHandler) {
        mBleScanHandler = bleScanHandler;
    }
    
    public void setNotifyHandler(NativeBleNotifyHandler bleNotifyHandler) {
        mBleNotifyHandler = bleNotifyHandler;
    }
    
    public void setBleEnableHandler(NativeBleEnableHandler bleEnableHandler) {
        mBleEnableHandler = bleEnableHandler;
    }
    
    public void setBleConnectStateHandler(NativeBleConnectStateHandler bleConnectStateHandler) {
        mBleConnectStateHandler = bleConnectStateHandler;
    }
    
    private final BroadcastReceiver mBleReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent == null) {
                boolean bluetoothOpened = BleManager.getClient().isBluetoothOpened();
                onBleEnableChanged(bluetoothOpened);
                return;
            }
            String action = intent.getAction();
            if (action != null) {
                if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
                    int blueState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
                    if (blueState == BluetoothAdapter.STATE_OFF) {
                        onBleEnableChanged(false);
                    } else if (blueState == BluetoothAdapter.STATE_ON) {
                        onBleEnableChanged(true);
                    }
                }
            }
        }
    };
    
    @Override
    public void setMethodCallHandler(@Nullable MethodCallHandler handler) {
        if (handler == null) {
            // 释放资源
            unRegisterBle();
            // 停止搜索蓝牙
            BleManager.getClient().stopSearch();
        }
        super.setMethodCallHandler(handler);
    }
}
